package edu.uts.reekie.designer.model;


import java.util.List;
import java.util.*;

import org.eclipse.swt.graphics.Image;

import org.eclipse.ui.views.properties.IPropertyDescriptor;
import org.eclipse.ui.views.properties.PropertyDescriptor;

import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import edu.uts.reekie.designer.Messages;

/*
 * 
 * TODO: remove concept of input/output connection points from this class
 * TODO: remove guides...what on earth are guides?
 * 
 * This class is a view element that can be connected 
 * 
 */
abstract public class ViewNode
	extends ViewElement implements LocatableAndSizable
{

private String id;
protected Point location = new Point(0,0);
static final long serialVersionUID = 1;
protected Dimension size = new Dimension(-1,-1);

protected static IPropertyDescriptor[] descriptors = null;
public static final String PROPERTY_SIZE = "size";         //$NON-NLS-1$
public static final String PROPERTY_LOCATION = "location"; //$NON-NLS-1$

/** Property ID to use when the list of outgoing connections is modified. */
public static final String PROPERTY_CONNECTORS_SOURCE = "connectors.source";
/** Property ID to use when the list of incoming connections is modified. */
public static final String PROPERTY_CONNECTORS_TARGET = "connectors.target";


static{
	descriptors = new IPropertyDescriptor[]{
		new PropertyDescriptor(PROPERTY_SIZE, Messages.PropertyDescriptor_LogicSubPart_Size),
		new PropertyDescriptor(PROPERTY_LOCATION,Messages.PropertyDescriptor_LogicSubPart_Location),
		// TODO: use messages instead of literals
		new PropertyDescriptor(PROPERTY_CONNECTORS_SOURCE,"source connectors"),
		new PropertyDescriptor(PROPERTY_CONNECTORS_TARGET,"target connectors")
	};
}

/** List of outgoing Connections. */
private List<ViewElement> sourceConnections = new ArrayList<ViewElement>();
/** List of incoming Connections. */
private List<ViewElement> targetConnections = new ArrayList<ViewElement>();


public ViewNode() {
	setID(getNewID());
}


// new connection methods
public void addConnection(AbstractConnector conn) {
	if (conn == null || conn.getSource() == conn.getTarget()) {
		throw new IllegalArgumentException();
	}
	if (conn.getSource() == this) {
		sourceConnections.add(conn);
		firePropertyChange(PROPERTY_CONNECTORS_SOURCE, null, conn);
	} else if (conn.getTarget() == this) {
		targetConnections.add(conn);
		firePropertyChange(PROPERTY_CONNECTORS_TARGET, null, conn);
	}
}

public void removeConnection(AbstractConnector conn){
	if (conn == null) {
		throw new IllegalArgumentException();
	}
	if (conn.getSource() == this) {
		sourceConnections.remove(conn);
		firePropertyChange(PROPERTY_CONNECTORS_SOURCE, null, conn);
	} else if (conn.getTarget() == this) {
		targetConnections.remove(conn);
		firePropertyChange(PROPERTY_CONNECTORS_TARGET, null, conn);
	}
}

/**
 * Return a List of outgoing Connections.
 */
public List<ViewElement> getSourceConnections() {
	return new ArrayList<ViewElement>(sourceConnections);
}

/**
 * Return a List of incoming Connections.
 */
public List<ViewElement> getTargetConnections() {
	return new ArrayList<ViewElement>(targetConnections);
}

/*
public Vector getConnections() {
	Vector v = (Vector)outputs.clone();
	Enumeration ins = inputs.elements();
	while (ins.hasMoreElements())
		v.addElement(ins.nextElement());
	return v;
}
*/



public Image getIcon() {
	return getIconImage();
}

abstract public Image getIconImage();

public String getID() {
	return id; 
}


public Point getLocation() {
	return location;
}

abstract protected String getNewID();

/**
 * Returns useful property descriptors for the use
 * in property sheets. this supports location and
 * size.
 *
 * @return  Array of property descriptors.
 */
public IPropertyDescriptor[] getPropertyDescriptors() {
	return descriptors;
}


/**
 * Returns an Object which represents the appropriate
 * value for the property name supplied.
 *
 * @param propName  Name of the property for which the
 *                  the values are needed.
 * @return  Object which is the value of the property.
 */
public Object getPropertyValue(Object propName) {
	if (PROPERTY_SIZE.equals(propName))
		return new DimensionPropertySource(getSize());
	else if( PROPERTY_LOCATION.equals(propName))
		return new LocationPropertySource(getLocation());
	return null;
}

public Dimension getSize() {
	return size;
}



/**
 * 
 */
public boolean isPropertySet(){
	return true;	
}



/*
 * Does nothing for the present, but could be
 * used to reset the properties of this to 
 * whatever values are desired.
 *
 * @param id  Parameter which is to be reset.
 *
public void resetPropertyValue(Object id){
	if(PROPERTY_SIZE.equals(id)){;}
	if(PROPERTY_LOCATION.equals(id)){;}
}*/

public void setID(String s) {
	id = s;
}

public void setLocation(Point p) {
	if (location.equals(p))
		return;
	
	location = p;
	firePropertyChange(PROPERTY_LOCATION, null, p);  //$NON-NLS-1$
}

/*
protected void setOutput(String terminal, boolean val) {
	Enumeration elements = outputs.elements();
	Wire w;
	while (elements.hasMoreElements()) {
		w = (Wire) elements.nextElement();
		if (w.getSourceTerminal().equals(terminal) && this.equals(w.getSource()))
			w.setValue(val);
	}
}
*/

/**
 * Sets the value of a given property with the value
 * supplied. Also fires a property change if necessary.
 * 
 * @param id  Name of the parameter to be changed.
 * @param value  Value to be set to the given parameter.
 */
public void setPropertyValue(Object id, Object value){
	if (PROPERTY_SIZE.equals(id))
		setSize((Dimension)value);
	else if (PROPERTY_LOCATION.equals(id))
		setLocation((Point)value);
}

public void setSize(Dimension d) {
	if (size.equals(d))
		return;
	size = d;
	firePropertyChange(PROPERTY_SIZE, null, size);  //$NON-NLS-1$
}



}
